/**
 * -1. Линия развертки имеет разный цвет по всей длине
 * -2. Отметка от цели должна иметь вид дуги
 * +3. Д10 и А10 имеют вид пунктирных линий
 * +4. Цель движется слишком быстро
 * -5. Траектория длижения цели должна включать только пройденный путь
 * -6. Диалог выбора цвета отметок(один диалог для всех трех отметок или по одному для каждой?)
 * -7. Рисование отметок на двух последних обзорах
 * -8. Прерисовка отметки сразу после изменения масштаба, а не только после прохождения развертки
 * -9. Изменение размера ИКО при изменении размера окна, поддержка полноэкранного режима.
 * -10.Показывать дробные координаты азимута-дальности цели.
 * -11.Добавить массив целей.
 * -12.Добавить скорость, высоту, курс, тангаж, крен цели.
 *
 */

import java.util.ArrayList;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.*;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.EllipseBuilder;
import javafx.scene.shape.Line;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathBuilder;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;


public class IKO extends Application {

    private static int timeInterval = 30;
    protected static double clockRadius = 450;
    private static double offsetX = 120;      //hardcoded offset based on Lipetsk map
    private static double offsetY = 60;       //hardcoded offset hased on Lipetsk map
    private double centerX = clockRadius + offsetX;
    private double centerY = clockRadius + offsetY;
    private int maxDistance = 21; //current distance in km
    private Timeline razvertkaAnimation;
    private Line razvertkaLine;
    private Line altimeterLine;
    public static int time;
    private VBox vBox;
    private Circle circle;
    private Group root;
    protected boolean isRazvertkaSelected = true;
    protected boolean isAnimationPlaying = false;
    protected boolean isD50Selected = false;
    protected boolean isD10Selected = false;
    protected boolean isA30Selected = false;
    protected boolean isA10Selected = false;
    private boolean isBackground = true;

    private Line[] azimut30Lines = new Line[12];
    private Path[] azimut10Lines = new Path[36];
    private ArrayList<Ellipse> distance50Lines = new ArrayList<Ellipse>();
    private ArrayList<Ellipse> distance10Lines = new ArrayList<Ellipse>();
    private Label currentAngle = new Label("current angle ");
    protected boolean isShowPath;
    private Label label;
    private Label currentDistanceLabel;
    private Rectangle rec;
    private double targetAngle;
    protected int razvertkaAngle;
    private Arc[] arc = new Arc[10];
    private Arc arc2;
    private Arc arc3;
    private Color targetColor = Color.YELLOW;
    private Label infoLabel;
    private Stage stage;
    private Rectangle rectangle;
    private HBox hBox;
    private ArrayList<Timeline> targetAnimations = new ArrayList<Timeline>();
    private Timeline[] targetAnimation = new Timeline[10];
    private ImageView backgroundImageView;
    private Button showKGI;
    private IndicatorPosadki indicatorPosadki;
    public static int sharedTime;
    private Button showHeightIndikator;
    private Button showAltimeter;
    public int circleTime = 10;
    public static int altimeterAzimut = 300;
    private Label altimeterAzimutLabel;
    private Button altimeterAzimutPlus;
    private Button altimeterAzimutMinus;


    @Override
    public void start(Stage primaryStage) {
        // TODO Auto-generated method stub

        makeFullLayout(primaryStage);

        stage = primaryStage;
        //primaryStage.setFullScreen(true);
       recalcutateBasicConstants(primaryStage.getHeight());

        primaryStage.heightProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
                System.out.println("Height: " + newSceneHeight + " /n clockRadius: " + clockRadius);
                //recalcutateBasicConstants(newSceneHeight);
            }
        });

        showStageState(primaryStage);

        //showKGIndicator();
    }

    private void recalcutateBasicConstants(Number newSceneHeight) {
        System.out.println("recalcutateBasicConstants method called");
        clockRadius = newSceneHeight.intValue() / 2 - 20;
        if(isBackground){
            centerX = clockRadius + offsetX;
            centerY = clockRadius + offsetY;

        }else{
            centerX = clockRadius + 0;
            centerY = clockRadius + 0;
        }
        root.getChildren().removeAll(rectangle, hBox, arc[0]);

        rectangle = new Rectangle(0, 0, clockRadius * 2 + 20, clockRadius * 2 + 50);
        rectangle.setFill(Color.BLACK);
        Image image1 = new Image("lipetsk.png");
        backgroundImageView = new ImageView();
        backgroundImageView.setImage(image1);





        hBox = new HBox();

        if (isBackground) {
            hBox.getChildren().add(backgroundImageView);
        }else{
            hBox.getChildren().add(rectangle);
        }
        hBox.getChildren().add(addCheckBoxPanel());



        root.getChildren().addAll(hBox);
        addAzimutLines();
        updateDistanceLines();
        repaintD10();
        repaintD50();
        repaintAzimutLines();
        addRazvertkaAnimation();
        repaintRazvertka();

        createTargetAnimation(0);
        createTargetAnimation(1);
        createTargetAnimation(2);

    }

    private void makeFullLayout(Stage primaryStage) {
        System.out.println("makeFullLayout method called");
        primaryStage.setTitle("IKO");

        final Label labelFile = new Label();

        root = new Group();

        Line line2 = new Line(0, 0, centerY + clockRadius, 0);
        addAzimutLines();
        updateDistanceLines();

        vBox = new VBox(25);
        vBox.getChildren().addAll(line2);

        addRazvertkaAnimation();


        rectangle = new Rectangle(0, 0, clockRadius * 2, clockRadius * 2);
        rectangle.setFill(Color.BLACK);
        root.getChildren().add(rectangle);

        hBox = new HBox();

        hBox.getChildren().add(vBox);
        hBox.getChildren().add(addCheckBoxPanel());

        root.getChildren().add(hBox);
        addAltimeterLine();
        createTargetAnimation(0);
        createTargetAnimation(1);
        createTargetAnimation(2);

        primaryStage.setScene(new Scene(root));
        primaryStage.show();


    }

    private void addAltimeterLine() {
        altimeterLine = new Line(0, clockRadius * 1.2, 0, 0);
        altimeterLine.setStroke(Color.DARKGREEN);
        altimeterLine.setStrokeWidth(3);
        altimeterLine.setLayoutX(centerX - 80);
        altimeterLine.setLayoutY(centerY - 80);
        altimeterLine.getTransforms().add(new Rotate(120));
        root.getChildren().add(altimeterLine);
    }

    private void showStageState(Stage primaryStage) {
        infoLabel.setText("MaxHeight: " + primaryStage.getMaxHeight()
                + " MaxWidth: " + primaryStage.getMaxWidth()
                + " isFullScreen: " + primaryStage.isFullScreen()
                + " H: " + primaryStage.getHeight()
                + " W: " + primaryStage.getWidth());
    }

    private void addAzimutLines() {
        for (int i = 0; i < 12; i++) {
            azimut30Lines[i] = new Line(centerX, centerY,
                    (centerX + clockRadius * Math.sin(i * 30 * Math.PI / 180)),
                    (centerY + clockRadius * Math.cos(i * 30 * Math.PI / 180)));
            azimut30Lines[i].setStroke(Color.BLACK);
        }

        for (int i = 0; i < 36; i++) {
            Path path2 = PathBuilder.create().elements(
                    new MoveTo(centerX, centerY),
                    new LineTo((centerX + clockRadius * Math.sin(i * 10 * Math.PI / 180)),
                            (centerY + clockRadius * Math.cos(i * 10 * Math.PI / 180)))
            ).build();
            path2.setStroke(Color.BLACK);
            path2.setFill(Color.BLACK);
            path2.getStrokeDashArray().setAll(1d, 15d);

            azimut10Lines[i] = path2;
        }
    }


    private void addRazvertkaAnimation() {
        razvertkaLine = new Line(0, clockRadius * 1.7, 0, 0);
        razvertkaLine.setStroke(Color.DARKORANGE);
        razvertkaLine.setStrokeWidth(2);
        createRazvertkaAnimation();
    }

    private double getScaledDistance(double x) {
        return x * clockRadius / maxDistance;
    }

    private void createTargetAnimation(final int shadowNo) {
        System.out.println("createTargetAnimation " + shadowNo);
        if (root.getChildren().contains(arc)) {
            root.getChildren().remove(arc);
        }
        if (targetAnimation[shadowNo]!=null){
                targetAnimation[shadowNo].stop();
        }
        targetAnimation[shadowNo] = new Timeline();
        arc[shadowNo] = new Arc(centerX, centerY, 25, 25, 0, 20);
        arc[shadowNo].setFill(null);
        arc[shadowNo].setStroke(Color.rgb(255 - shadowNo * 80 ,255 - shadowNo * 80,0));
        arc[shadowNo].setStrokeWidth(1);
        root.getChildren().add(arc[shadowNo]);

        targetAnimation[shadowNo].stop();
        targetAnimation[shadowNo].getKeyFrames().add(new KeyFrame(Duration.millis(timeInterval),
                new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent actionEvent) {
                        repaintArc(arc[shadowNo], shadowNo);
                    }
                }));
        targetAnimation[shadowNo].setCycleCount(Animation.INDEFINITE);
        targetAnimation[shadowNo].play();
    }

    public void showTarget(Target target){

    }

    public void showTargets(Target[] targets){

    }

    private void repaintArc(Arc arc, int shadowNo) {
        double radius = getRadiusByTime(shadowNo);
        double height = getHeightByTime(shadowNo);
        double x = radius * Math.cos(time * Math.PI / 180 / 100);
        double y = radius * Math.sin(time * Math.PI / 180 / 100);
        double startAngle = getAngleByTime(shadowNo);
        double correctedAngle = 360 - (startAngle - 90) % 360;
        if (needRepaint(shadowNo)) {
            if(isInsideHidingKonus(height,radius)){
                drawArc(arc, radius, correctedAngle, shadowNo);
            }
            currentDistanceLabel.setText(
                    " A: " + (startAngle) + " deg"
                            + "\n D: " + Math.round(xy2D(x, y)) + " Km \n"
                            + Math.round(getScaledDistance(xy2D(x, y)) / 100));
            label.setText(" x: " + Math.round(x) + " km \n y: " + Math.round(y) + " km");
            //System.out.println(" x: " + Math.round(x) + " km \n y: " + Math.round(y) + " km");
            System.out.println(" D: " + radius + " A: " + startAngle);
            System.out.println("time: " + time/360 + " shared time: "+ sharedTime);
        }
        //System.out.println(" x: " + Math.round(x) + " km \n y: " + Math.round(y) + " km");

    }

    private double getAngleByTime(int shadowNo) {
        //return time/ 100.0;
        if((sharedTime - circleTime * shadowNo * 1000)>0){
            return Nalet.points[getIndex(shadowNo) + 1];
        }else{
            return Nalet.points[1];
        }
    }

    private void forceRepaintArc(Arc arc, int shadowNo) {
        double radius = getRadiusByTime(shadowNo);
        double x = radius * Math.cos(time * Math.PI / 180.0 / 100.0);
        double y = radius * Math.sin(time * Math.PI / 180.0 / 100.0);
        double startAngle = getAngleByTime(shadowNo);
        double correctedAngle = 360 - (startAngle - 90) % 360;
        drawArc(arc, radius, correctedAngle, shadowNo);
        currentDistanceLabel.setText(
                " A: " + (startAngle) + " deg"
                        + "\n D: " + Math.round(xy2D(x, y)) + " Km \n"
                        + Math.round(getScaledDistance(xy2D(x, y)) / 100));
        label.setText(" x: " + Math.round(x) + " km \n y: " + Math.round(y) + " km");
    }

    private double getRadiusByTime(int shadowNo) {
        //return Math.min(400, time / 360.0 * 1 + 2);
        if((sharedTime - circleTime * shadowNo * 1000)>0){
            return Nalet.points[getIndex(shadowNo) + 0] / 1000 ;
        }else{
            return Nalet.points[0];
        }
    }

    private int getIndex(int shadowNo) {
        return 5 * ((sharedTime - circleTime * shadowNo * 1000) / 1000) % Nalet.points.length;
    }

    private double getHeightByTime(int shadowNo) {
        //return Math.min(400, time / 360.0 * 1 + 2);
        if((sharedTime - circleTime * shadowNo * 1000)>0){
            return Nalet.points[getIndex(shadowNo) + 4] / 1000 ;
        }else{
            return Nalet.points[4];
        }
    }

    private boolean needRepaint(int shadowNo) {
        double radius = getRadiusByTime(shadowNo);
        double x = radius * Math.cos(time * Math.PI / 180 / 100);
        double y = radius * Math.sin(time * Math.PI / 180 / 100);
        double startAngle = getAngleByTime(shadowNo);
        double correctedAngle = 360 - (startAngle - 90) % 360;
        boolean result = Math.abs(razvertkaAngle - correctedAngle) < 3;
        targetAngle = radToDeg(xy2A(x, y));
        razvertkaAngle = 360 - (time - 90) % 360;
        return result;
    }

    private void drawArc(Arc arc, double radius, double startAngle, int shadowNo) {
        arc.setRadiusX(getScaledDistance(radius));
        arc.setRadiusY(getScaledDistance(radius));
        arc.setStrokeWidth(getScaledWidth(radius));
        arc.setStartAngle(startAngle - 3);
        arc.setLength(6.0);
        arc.setStroke(targetColor);
        if(shadowNo==1){
            arc.setStroke(targetColor.darker());
        }
        if(shadowNo==2){
            arc.setStroke(targetColor.darker().darker());
        }

    }

    private long getScaledWidth(double x, double y) {
        return Math.round(getScaledDistance(xy2D(x, y)) / 70 + 1);
    }

    private long getScaledWidth(double radius) {
        return Math.round(getScaledDistance(radius) / 70 + 1);
    }

    private double radToDeg(double a) {
        return (a > 0) ? (a * 180 / Math.PI) : (a * 180 / Math.PI + 2 * 180);
    }

    private void createRazvertkaAnimation() {
        if (razvertkaAnimation != null) {
            razvertkaAnimation.stop();
        }
        razvertkaAnimation = new Timeline();
        razvertkaLine.setLayoutX(centerX);
        razvertkaLine.setLayoutY(centerY);
        razvertkaLine.setFill(null);
        razvertkaAnimation.getKeyFrames().add(new KeyFrame(Duration.millis(timeInterval),
                new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent actionEvent) {
                        rotateRazvertka();
                    }
                }));
        razvertkaAnimation.setCycleCount(Animation.INDEFINITE);
        razvertkaAnimation.play();
    }

    private void rotateRazvertka() {
        if (time == 0) {
            razvertkaLine.getTransforms().add(new Rotate(180));
        } else {
            razvertkaLine.getTransforms().add(new Rotate(1));
        }
        time++;
        sharedTime += timeInterval;
        currentAngle.setText("current angle: " + time % 360);
    }

    private void updateDistanceLines() {
        int scaleFactor50 = maxDistance / 50;
        int scaleFactor10 = maxDistance / 10;
        double scaleX = clockRadius / maxDistance;
        distance50Lines.clear();
        distance10Lines.clear();
        for (int i = 0; i <= scaleFactor50; i++) {
            distance50Lines.add(createSolidEllipsePath(centerX,
                    centerY, i * 50 * scaleX, i * 50 * scaleX, 0));
        }

        for (int i = 0; i <= scaleFactor10; i++) {
            distance10Lines.add(
                    createEllipsePath(centerX,
                            centerY, i * 10 * scaleX, i * 10 * scaleX, 0));
        }
    }

    private void updateDistance50Lines() {
        int scaleFactor50 = maxDistance / 50;
        int scaleFactor10 = maxDistance / 10;
        double scaleX = clockRadius / maxDistance;
        distance50Lines.clear();
        for (int i = 0; i <= scaleFactor50; i++) {
            distance50Lines.add(createSolidEllipsePath(centerX,
                    centerY, i * 50 * scaleX, i * 50 * scaleX, 0));
        }

    }

    private void updateDistance10Lines() {
        int scaleFactor50 = maxDistance / 50;
        int scaleFactor10 = maxDistance / 10;
        double scaleX = clockRadius / maxDistance;
        distance10Lines.clear();

        for (int i = 0; i <= scaleFactor10; i++) {
            distance10Lines.add(createEllipsePath(centerX,
                    centerY, i * 10 * scaleX, i * 10 * scaleX, 0));
        }
    }

    private Ellipse createSolidEllipsePath(
            double centerX,
            double centerY,
            double radiusX,
            double radiusY,
            double rotate) {
        Path path = new Path();
        Ellipse bigCircle = EllipseBuilder.create()
                .centerX(centerX)
                .centerY(centerY)
                .radiusX(radiusX)
                .radiusY(radiusY)
                .strokeWidth(1)
                .stroke(Color.BLACK)
                .fill(null)
                .build();
//        path.getStrokeDashArray().setAll(5d, 5d);
        return bigCircle;
    }

    private Ellipse createEllipsePath(
            double centerX,
            double centerY,
            double radiusX,
            double radiusY,
            double rotate) {
        Path path = new Path();
        Ellipse bigCircle = EllipseBuilder.create()
                .centerX(centerX)
                .centerY(centerY)
                .radiusX(radiusX)
                .radiusY(radiusY)
                .strokeWidth(1)
                .stroke(Color.BLACK)
                .fill(null)
                .strokeDashArray(1d, 5d)
                .build();
        return bigCircle;
    }

    private double xy2A(double x, double y) {
        return Math.atan2(y, x);
    }

    private double xy2D(double x, double y) {
        return Math.sqrt(x * x + y * y);
    }

    public static void main(String[] args) {
        launch(args);
    }

    private VBox addCheckBoxPanel() {
        VBox vbox = new VBox();
        vbox.setSpacing(10);
        final ComboBox<String> scaleComboBox = addScaleComboBox();
        final ComboBox<String> intervalComboBox = addIntervalComboBox();
        final CheckBox cb1 = addRazvertkaCheckBox();
        final CheckBox cb2 = addD50CheckBox();
        final CheckBox cb3 = addD10CheckBox();
        final CheckBox cb4 = addA30CheckBox();
        final CheckBox cb5 = addA10CheckBox();
        final CheckBox cb6 = addBackgroundCheckBox();

        altimeterAzimutLabel = new Label("Altimeter : " + altimeterAzimut);
        altimeterAzimutPlus = new Button("+");
        altimeterAzimutPlus.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                altimeterAzimut = (altimeterAzimut + 1) % 360;
                altimeterAzimutLabel.setText("Altimeter: " + altimeterAzimut);
                root.getChildren().remove(altimeterLine);
                altimeterLine.getTransforms().add(new Rotate(1));
                root.getChildren().add(altimeterLine);
            }
        });
        altimeterAzimutMinus = new Button("-");
        altimeterAzimutMinus.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                altimeterAzimut = (altimeterAzimut - 1) % 360;
                altimeterAzimutLabel.setText("Altimeter: " + altimeterAzimut);
                root.getChildren().remove(altimeterLine);
                altimeterLine.getTransforms().add(new Rotate(-1));
                root.getChildren().add(altimeterLine);
            }
        });

        showKGI = new Button("Kurs-Glissada Indikator");
        showKGI.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                showKGIndicator();
            }
        });

        //showHeightIndikator = new Button("Height Indikator");
        //showHeightIndikator.setOnAction(new EventHandler<ActionEvent>() {
        //    public void handle(ActionEvent event) {
        //        showKGIndicator();
        //    }
        //});

        showAltimeter = new Button("Altimeter");
        showAltimeter.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                showAltimeter();
            }
        });


        final ColorPicker colorPicker = new ColorPicker();
        colorPicker.setValue(Color.YELLOW);
        infoLabel = new Label("info: ");
        label = new Label("coordinates: ");
        label.setTextFill(Color.BLACK);
        colorPicker.setOnAction(new EventHandler() {
            @Override
            public void handle(Event arg0) {
                targetColor = colorPicker.getValue();
                forceRepaintArc(arc[0], 0);
            }
        });
        currentDistanceLabel = new Label("D: ");
        vbox.getChildren().addAll(cb1, intervalComboBox, cb2, cb3, cb4, cb5, scaleComboBox,
                currentAngle,
                label,
                currentDistanceLabel,
                //cb6, //image background with map
                colorPicker,
                showKGI,
                showAltimeter,
                altimeterAzimutLabel,
                altimeterAzimutMinus,
                altimeterAzimutPlus

        );
        return vbox;
    }

    private void showKGIndicator() {
        Stage newStage = new Stage();
        indicatorPosadki = new IndicatorPosadki();
        indicatorPosadki.makeBasicLayout(newStage);
        newStage.setScene(new Scene(indicatorPosadki.createRoot()));
        newStage.show();
    }

    private void showAltimeter(){
        Stage newStage = new Stage();
        Altimeter altimeter = new Altimeter();
        altimeter.makeBasicLayout(newStage);
        newStage.setScene(new Scene(altimeter.createRoot()));
        newStage.show();
    }

    private CheckBox addBackgroundCheckBox() {
        final CheckBox cb6 = new CheckBox("Image Background");
        cb6.setSelected(isBackground);
        cb6.selectedProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> observable,
                                Boolean oldValue, Boolean newValue) {
                // TODO Auto-generated method stub
                System.out.println("cb6 changed to " + cb6.isSelected());
                isBackground = cb6.isSelected();
                if (isBackground) {
                    root.getChildren().addAll(backgroundImageView);
                } else {
                    root.getChildren().removeAll(backgroundImageView);
                }
            }

        });
        return cb6;
    }


    private CheckBox addA10CheckBox() {
        final CheckBox cb5 = new CheckBox("A10");
        cb5.setSelected(isA10Selected);
        cb5.selectedProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> observable,
                                Boolean oldValue, Boolean newValue) {
                // TODO Auto-generated method stub
                System.out.println("cb5 changed to " + cb5.isSelected());
                isA10Selected = cb5.isSelected();
                if (isA10Selected) {
                    root.getChildren().addAll(azimut10Lines);
                } else {
                    root.getChildren().removeAll(azimut10Lines);
                }
            }

        });
        return cb5;
    }

    private CheckBox addA30CheckBox() {
        final CheckBox cb4 = new CheckBox("A30");
        cb4.setSelected(isA30Selected);
        cb4.selectedProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> observable,
                                Boolean oldValue, Boolean newValue) {
                // TODO Auto-generated method stub
                System.out.println("cb4 changed to " + cb4.isSelected());
                isA30Selected = cb4.isSelected();
                repaintAzimutLines();
            }

        });
        return cb4;
    }

    private void repaintAzimutLines() {
        if (isA30Selected) {
            root.getChildren().removeAll(azimut30Lines);
            addAzimutLines();
            root.getChildren().addAll(azimut30Lines);
        } else {
            root.getChildren().removeAll(azimut30Lines);
        }
    }

    private CheckBox addD10CheckBox() {
        final CheckBox cb3 = new CheckBox("D10");
        cb3.setSelected(isD10Selected);
        cb3.selectedProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> observable,
                                Boolean oldValue, Boolean newValue) {
                // TODO Auto-generated method stub
                System.out.println("cb3 changed to " + cb3.isSelected());
                isD10Selected = cb3.isSelected();
                if (isD10Selected) {
                    updateDistance10Lines();
                    root.getChildren().addAll(distance10Lines);
                } else {
                    root.getChildren().removeAll(distance10Lines);
                    updateDistance10Lines();
                }
            }

        });
        return cb3;
    }

    private CheckBox addD50CheckBox() {
        final CheckBox cb2 = new CheckBox("D50");
        cb2.setSelected(isD50Selected);
        cb2.selectedProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> observable,
                                Boolean oldValue, Boolean newValue) {
                // TODO Auto-generated method stub
                System.out.println("cb2 changed to " + cb2.isSelected());
                isD50Selected = cb2.isSelected();
                if (isD50Selected) {
                    updateDistance50Lines();
                    root.getChildren().addAll(distance50Lines);
                } else {
                    root.getChildren().removeAll(distance50Lines);
                    updateDistance50Lines();
                }

            }

        });
        return cb2;
    }

    private CheckBox addRazvertkaCheckBox() {
        final CheckBox cb1 = new CheckBox("Razvertka, s");
        cb1.setSelected(isRazvertkaSelected);
        cb1.selectedProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> observable,
                                Boolean oldValue, Boolean newValue) {
                // TODO Auto-generated method stub
                System.out.println("cb1 changed to " + cb1.isSelected());
                isRazvertkaSelected = cb1.isSelected();
                repaintRazvertka();

            }

        });
        return cb1;
    }

    private void repaintRazvertka() {
        if (isRazvertkaSelected) {
            root.getChildren().add(razvertkaLine);
        } else {
            root.getChildren().remove(razvertkaLine);
        }
    }

    private ComboBox<String> addScaleComboBox() {
        final ComboBox<String> scaleComboBox = new ComboBox<String>();
        scaleComboBox.getItems().addAll(
                "2",
                "5",
                "10",
                "21",
                "50",
                "100",
                "150",
                "200",
                "250",
                "300",
                "350",
                "400"
        );
        scaleComboBox.setValue("21");
        scaleComboBox.valueProperty().addListener(new ChangeListener<String>() {

            @Override
            public void changed(ObservableValue<? extends String> arg0,
                                String arg1, String arg2) {
                // TODO Auto-generated method stub
                System.out.println("selected getScaledDistance is " + arg1 + arg2);
                maxDistance = Integer.parseInt(arg2);
                repaintD10();
                repaintD50();
                forceRepaintArc(arc[0], 0);
            }

        });
        return scaleComboBox;
    }

    private void repaintD10() {
        if (isD10Selected) {
            root.getChildren().removeAll(distance10Lines);
            updateDistance10Lines();
            root.getChildren().addAll(distance10Lines);
        }
    }

    private void repaintD50() {
        if (isD50Selected) {
            root.getChildren().removeAll(distance50Lines);
            updateDistance50Lines();
            root.getChildren().addAll(distance50Lines);
        }
    }

    private ComboBox<String> addIntervalComboBox() {
        final ComboBox<String> intervalComboBox = new ComboBox<String>();
        intervalComboBox.getItems().addAll(
                "7",
                "10",
                "15",
                "18",
                "20",
                "30",
                "60",
                "72"
        );
        intervalComboBox.setValue("10");
        intervalComboBox.valueProperty().addListener(new ChangeListener<String>() {

            @Override
            public void changed(ObservableValue<? extends String> arg0,
                                String arg1, String arg2) {
                // in ms per 1 degree
                timeInterval = Integer.parseInt(arg2) * 1000 / 360;
                // full circle in seconds
                circleTime = Integer.parseInt(arg2);
                System.out.println("selected getScaledDistance is " + arg1 + arg2 + " t: " + timeInterval);
                razvertkaAnimation.stop();
                createRazvertkaAnimation();

            }

        });
        return intervalComboBox;
    }

    private boolean isInsideHidingKonus(double h, double d){
        return d>h/Math.sin(26.0/180*Math.PI);
    }

}
